import { Octokit } from 'octokit'
import ky from 'ky'
import { useCallback } from 'react'
import { DataStorePlatform } from '@/types'
import { syncConfigAtom, activeSyncTypeAtom } from '@/atoms'
import { useAtomValue } from 'jotai'

interface httpResponse {
  status?: string
  url?: unknown
  exported_items?: string
}

export default function useSyncData() {
  const { uploadtoGist, downloadFromGist } = useGist()
  const { uploadtoJsonStorage, downloadFromJsonStorage } = useJsonStorage()
  const platform = useAtomValue(syncConfigAtom)
  const activeSyncType = useAtomValue(activeSyncTypeAtom)

  const uploadData = useCallback(async (data: unknown) => {
    if (activeSyncType === DataStorePlatform.Gist) {
      return uploadtoGist(data, platform[activeSyncType])
    }
    else if (activeSyncType === DataStorePlatform.JsonStorage) {
      return uploadtoJsonStorage(data, platform[activeSyncType])
    }
  }, [platform, activeSyncType])

  const downloadData = useCallback(async () => {
    if (activeSyncType === DataStorePlatform.Gist) {
      return downloadFromGist(platform[activeSyncType])
    }
    else if (activeSyncType === DataStorePlatform.JsonStorage) {
      return downloadFromJsonStorage(platform[activeSyncType])
    }
  }, [platform])

  return {
    uploadData,
    downloadData,
  }
}

// https://github.com/octokit/core.js#readme
// https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#about-gists
function useGist() {
  const fileName = 'moss.json'
  const fileDesc = 'configs for Moss(http://moss.tuzki.cc)'

  const createGist = async (octokit: Octokit) => {
    const res = await octokit.request('POST /gists', {
      description: fileDesc,
      public: false,
      files: {
        [fileName]: {
          content: '{}',
        },
      },
    })
    return res.data
  }

  const getGist = async (octokit: Octokit) => {
    const res = await octokit.request('GET /gists')
    if (res.status === 200) {
      const usedGist = res.data.find((item) => {
        return item.files[fileName]?.filename === fileName
      })
      return usedGist
    }
    else {
      return null
    }
  }

  const updateGist = async (octokit: Octokit, gist_id: string, data: unknown) => {
    return await octokit.request(`PATCH /gists/${gist_id}`, {
      files: {
        [fileName]: {
          content: JSON.stringify(data),
        },
      },
    })
  }

  const uploadtoGist = async (data: unknown, config: { url: string, key: string }) => {
    const octokit = new Octokit({
      auth: config.key,
    })
    const usedGist = await getGist(octokit)
    if (!usedGist) {
      const newdGist = await createGist(octokit)
      await updateGist(octokit, newdGist.id!, data) // why newdGist.id undefined
    }
    else {
      await updateGist(octokit, usedGist.id, data)
    }
  }

  const downloadFromGist = async (config: { url: string, key: string }) => {
    const octokit = new Octokit({
      auth: config.key,
    })
    const usedGist = await getGist(octokit)
    return usedGist && usedGist.files[fileName].raw_url
      ? await ky(usedGist.files[fileName].raw_url).text()
      : null
  }

  return {
    uploadtoGist,
    downloadFromGist,
  }
}

function useJsonStorage() {
  const uploadtoJsonStorage = async (data: unknown, config: { url: string, key: string }) => {
    if (!config.key || !config.url) {
      return false
    }
    const res: httpResponse = await ky(config.url, {
      method: 'PUT',
      searchParams: {
        apiKey: config.key,
      },
      body: JSON.stringify(data),
      headers: {
        'content-type': 'application/json',
      },
    }).json()
    return !!res.url
  }

  const downloadFromJsonStorage = async (config: { url: string, key: string }) => {
    if (!config.url)
      return false
    const res: httpResponse = await ky(config.url, {
      method: 'GET',
    }).json()
    return res
  }
  return {
    downloadFromJsonStorage,
    uploadtoJsonStorage,
  }
}
